package com.music.glide.load.engine.bitmap_recycle

import android.content.ComponentCallbacks2
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
import android.util.Log
import kotlin.math.round

class KLruBitmapPool(
    private var maxSize: Long,
    private val strategy: KLruPoolStrategy = getDefaultStrategy(),
    private val allowedConfigs: Set<Bitmap.Config?> = getDefaultAllowedConfigs()
) : KBitmapPool {

    private var initialMaSize = 0L

    private var tracker: BitmapTracker = NullBitmapTracker()

    private var currentSize = 0L

    private var hits = 0

    private var misses = 0

    private var puts = 0

    private var evictions = 0

    fun hitCount() = hits

    fun missCount() = misses

    fun putCount() = puts

    fun evictions() = evictions

    fun getCurrentSize() = currentSize

    override fun getMaxSize(): Long {
        return maxSize
    }

    override fun setSizeMultiplier(sizeMultiplier: Float) {
        maxSize = round(initialMaSize * sizeMultiplier).toLong()
        evict()
    }

    override fun put(bitmap: Bitmap?) {

        if (bitmap == null || bitmap.isRecycled) {
            throw IllegalStateException("bitmap not be null or recycled")
        }

        if (!bitmap.isMutable || strategy.getSize(bitmap) > maxSize || !allowedConfigs.contains(
                bitmap.config
            )
        ) {
            Log.d(
                TAG, """Reject bitmap from pool, 
                    bitmap: ${strategy.logBitmap(bitmap)},
                    is mutable: ${bitmap.isMutable}
                    is allowed config:${allowedConfigs.contains(bitmap.config)}"""
            )
            bitmap.recycle()
            return
        }
        val size = strategy.getSize(bitmap)
        strategy.put(bitmap)
        tracker.add(bitmap)
        puts++
        currentSize += size
        Log.d(TAG, "put bitmap in Pool:${strategy.logBitmap(bitmap)}")
        dump()
        evict()
    }

    override fun get(width: Int, height: Int, config: Bitmap.Config?): Bitmap {
        var result = getDirtyOrNUll(width, height, config)
        if (result == null) {
            result = createBitmap(width, height, config)
        } else {
            result.eraseColor(Color.TRANSPARENT)
        }
        return result
    }

    override fun getDirty(width: Int, height: Int, config: Bitmap.Config): Bitmap {
        return getDirtyOrNUll(width, height, config) ?: createBitmap(width, height, config)
    }

    override fun clearMemory() {
        Log.d(TAG, "clearMemory")
        trimToSize(0)
    }

    override fun trimMemory(level: Long) {
        Log.d(TAG, "trimMemory, level=$level")
        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND ||
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
            level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
        ) {
            clearMemory()
        } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN ||
            level.toInt() == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
        ) {
            trimToSize((getMaxSize() / 2).toInt())
        }
    }
    @Synchronized
    private fun trimToSize(size: Int) {
        while (currentSize > size) {
            val removed = strategy.removeLast()
            if (removed == null) {
                Log.d(TAG, "Size mismatch, resetting")
                dumpUnchecked()
                currentSize = 0
            } else {
                tracker.remove(removed)
                currentSize -= strategy.getSize(removed)
                evictions++
                Log.d(TAG, "Evicting bitmap=" + strategy.logBitmap(removed))
                dump()
                removed.recycle()
            }
        }
    }

    @Synchronized
    private fun getDirtyOrNUll(width: Int, height: Int, config: Bitmap.Config?):Bitmap? {
        assertNoHardwareConfig(config)
        val result = strategy.get(width, height, config ?: DEFAULT_CONFIG)
        if (result == null) {
            Log.d(TAG, "Missing bitmap= ${strategy.logBitmap(width, height, config)}")
            misses++
        } else {
            hits++
            currentSize -= strategy.getSize(result)
            tracker.remove(result)
            normalize(result)
        }
        Log.d(TAG, "Get Bitmap ${strategy.logBitmap(width, height, config)}")
        dump()
        return result
    }

    private fun evict() {
        trimMemory(maxSize)
    }

    private fun dump() {
        dumpUnchecked()
    }

    private fun dumpUnchecked() {
        Log.v(
            TAG, """
            Hits=$hits, 
            misses=$misses, 
            puts=$puts, 
            evictions=$evictions, 
            currentSize=$currentSize, 
            maxSize=$maxSize
            Strategy=$strategy
            """
        )
    }

    private interface BitmapTracker {

        fun add(bitmap: Bitmap)

        fun remove(bitmap: Bitmap)
    }

    private class NullBitmapTracker : BitmapTracker{

        override fun add(bitmap: Bitmap) {

        }

        override fun remove(bitmap: Bitmap) {

        }
    }

    companion object {

        const val TAG = "KLruBitmapPool"

        val DEFAULT_CONFIG = Bitmap.Config.ARGB_8888

        fun getDefaultStrategy(): KLruPoolStrategy {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                KSizeConfigStrategy()
            } else {
                KAttributeStrategy()
            }
        }

        fun getDefaultAllowedConfigs(): Set<Bitmap.Config?> {
            return Bitmap.Config.values().toMutableSet<Bitmap.Config?>().apply {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    // GIFs, among other types, end up with a native Bitmap config that doesn't map to a java
                    // config and is treated as null in java code. On KitKat+ these Bitmaps can be reconfigured
                    // and are suitable for re-use.
                    add(null)
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    remove(Bitmap.Config.HARDWARE)
                }
            }
        }

        fun assertNoHardwareConfig(config: Bitmap.Config?) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                return
            }
            if (config == Bitmap.Config.HARDWARE) {
                throw IllegalStateException(
                    """Cannot create a mutable Bitmap with
                           config:${config}
                           Consider setting Downsampler#ALLOW_HARDWARE_CONFIG to false in your
                           RequestOptions and/or in GlideBuilder.setDefaultRequestOptions"""
                )
            }
        }


        // 设置这两个值提供的位图本质上与返回的位图相同
        // 来自 Bitmap.createBitmap
        fun normalize(bitmap: Bitmap){
            bitmap.setHasAlpha(true)
            maybeSetPreMultiplied(bitmap)
        }

        private fun maybeSetPreMultiplied(bitmap: Bitmap) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                bitmap.isPremultiplied = true
            }
        }
        private fun createBitmap(width: Int,height: Int,config:Bitmap.Config?):Bitmap {
            return Bitmap.createBitmap(width, height, config ?: DEFAULT_CONFIG)
        }
    }
}